#include <vector>
#include <utility>
#include "log.h"
#include "pipe_config.h"
#include "markupstl.h"
#include "http_info.hpp"
#include "pipe_config.h"

namespace anet {
	namespace pipe {
		// ============================================
		// xml config description name.
		static const char* gConfig = "config";
		static const char* gConnections = "connections";
		static const char* gServer = "server";
		static const char* gId = "id";
		static const char* gConnection = "connection";
		static const char* gMode = "mode";
		static const char* gAddr = "addr";
		static const char* gPort = "port";
		static const char* gToken = "token";
		static const char* gItem = "item";
		static const char* gConnect = "connect";
		static const char* gListen = "listen";
		// ============================================

		// transfer pipe id(region-group-type-index) to uint32 type.
		inline std::pair<bool, uint32> transferPipeId(const std::string& id) {
			std::vector<std::string> vecValue;
			anet::http::strSplits(id, "-", vecValue);

			uint32 ret = 0;
			if (vecValue.size() != 4) {
				return { false,0 };
			}
			for (size_t i = 0; i < vecValue.size(); i++) {
				uint32 val = std::atoi(vecValue[i].c_str());
				if (val > 255) {
					return { false,0 };
				}
				ret += (val << (i * 8));
			}
			return { true,ret };
		}

		CPipeConfig::CPipeConfig() : m_id(0) {
			m_connects.clear();
			m_listens.clear();
		}

		CPipeConfig::~CPipeConfig() {
			m_connects.clear();
			m_listens.clear();
		}

		bool CPipeConfig::listenNodeCheck(const listenInfo& listenNode) const {
			for (size_t i = 0;i < m_listens.size();i++) {
				if (m_listens[i].addr == listenNode.addr && 
					m_listens[i].port == listenNode.port) {
					return true;
				}
			}
			return false;
		}

		bool CPipeConfig::connectNodeCheck(const connectInfo& connectNode) const {
			for (size_t i = 0;i < m_connects.size();i++) {
				if (m_connects[i].serverId == connectNode.serverId) {
					return true;
				}
			}
			return false;
		}

		bool CPipeConfig::Load(const char* filePath) {
			CMarkupSTL markup;
			if (!markup.Load(filePath)) {
				LogAdebug("load {} file error:{}", filePath, markup.GetError());
				return false;
			}

			// config node.
			if (!markup.FindElem(gConfig) || !markup.IntoElem()) {
				LogAdebug("can not find {} error", gConfig);
				return false;
			}

			// connections node
			if (!markup.FindElem(gConnections) || !markup.IntoElem()) {
				LogAdebug("can not find {} node error", gConnections);
				return false;
			}

			// server id.
			if (!markup.FindElem(gServer)) {
				LogAdebug("can not find {} node", gServer);
				return false;
			}
			auto localId = markup.GetAttrib(gId);
			auto localIdPair = transferPipeId(localId);
			if (!localIdPair.first) {
				LogAdebug("transfer local pipe id: {} error", localId);
				return false;
			}
			m_id = localIdPair.second;

			// try to iterator the connection node for connection or listen.
			while (markup.FindElem(gConnection)) {
				auto value = markup.GetAttrib(gMode);
				if (value == gListen) {
					// listen node
					listenInfo info;
					info.addr  = markup.GetAttrib(gAddr);
					info.port  = (uint16)std::atoi(markup.GetAttrib(gPort).c_str());
					info.token = markup.GetAttrib(gToken);
					if (listenNodeCheck(info)) {
						LogAdebug("find {}:{} listen node is repeated", info.addr, info.port);
						return false;
					}
					m_listens.push_back(info);
				} else if (value == gConnect) {
					// connect node
					markup.IntoElem();
					while (markup.FindElem(gItem)) {
						connectInfo info;
						auto serverId = markup.GetAttrib(gId);
						auto ret = transferPipeId(serverId);
						if (!ret.first) {
							LogAdebug("transfer connection server id:{} error", serverId);
							return false;
						}
						
						info.serverId = ret.second;
						info.addr = markup.GetAttrib(gAddr);
						info.port = (uint16)std::atoi(markup.GetAttrib(gPort).c_str());
						info.token= markup.GetAttrib(gToken);
						if (connectNodeCheck(info)) {
							LogAdebug("find {}({}:{}) connect node is repeated", info.serverId, info.addr, info.port);
							return false;
						}
						m_connects.push_back(info);
					}
				} else {
					LogAdebug("find invalid {} mode", value);
				}
			}

			// there must be connect node or listen node.
			return !(m_connects.empty() && m_listens.empty());
		}

		uint32 CPipeConfig::GetServerId() const {
			return m_id;
		}

		const std::vector<listenInfo>& CPipeConfig::GetListens() const {
			return m_listens;
		}

		const std::vector<connectInfo>& CPipeConfig::GetConnects() const {
			return m_connects;
		}
	}
}